package com.google.code.rees.scope.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import org.apache.struts2.util.ClassLoaderUtils;

import com.opensymphony.xwork2.ActionSupport;

/**
 * A utility for obtaining inherited fields, methods, and annotations, and
 * making
 * fields accessible.
 * 
 * @author rees.byars
 */
public class ReflectionUtil {

    public static Set<Field> getFields(Class<?> clazz) {
        Set<Field> fields = new HashSet<Field>();
        fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            if (!superClass.equals(Object.class)
                    && !superClass.equals(ActionSupport.class)) {
                fields.addAll(getFields(superClass));
            }
        }
        return fields;
    }

    public static Set<Method> getMethods(Class<?> clazz) {
        Set<Method> methods = new HashSet<Method>();
        methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            if (!superClass.equals(Object.class)
                    && !superClass.equals(ActionSupport.class)) {
                methods.addAll(getMethods(superClass));
            }
        }
        return methods;
    }

    public static Set<Class<?>> getClasses(Class<?> clazz) {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        classes.addAll(Arrays.asList(clazz.getDeclaredClasses()));
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            classes.addAll(getClasses(superClass));
        }
        return classes;
    }

    public static <T extends Annotation> Set<T> getAnnotationInstances(
            Class<?> clazz, Class<T> annotationClass) {
        Set<T> annotationInstances = new HashSet<T>();
        for (Class<?> clazzClass : clazz.getInterfaces()) {
            if (clazzClass.isAnnotationPresent(annotationClass)) {
                annotationInstances.add(clazzClass
                        .getAnnotation(annotationClass));
            }
        }
        if (clazz.isAnnotationPresent(annotationClass)) {
            annotationInstances.add(clazz.getAnnotation(annotationClass));
        }
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            annotationInstances.addAll(getAnnotationInstances(superClass,
                    annotationClass));
        }
        return annotationInstances;
    }

    public static void makeAccessible(Field field) {
        if ((Modifier.isPublic(field.getModifiers()))
                && (Modifier.isPublic(field.getDeclaringClass().getModifiers()))) {
            return;
        }
        field.setAccessible(true);
    }

    public static boolean classExists(String className) {
        boolean exists = false;
        try {
            ClassLoaderUtils.loadClass(className, ReflectionUtil.class);
            exists = true;
        } catch (ClassNotFoundException e) {
        }
        return exists;
    }

}
